﻿using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Emgu.CV.UI;
using System.Windows.Forms;
using Emgu.CV.Util;

namespace EmguCV.WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, IDisposable
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void image1_Initialized(object sender, EventArgs e)
        {
            Mat image = new Mat(100, 400, DepthType.Cv8U, 3);
            image.SetTo(new Bgr(255, 255, 255).MCvScalar);
            CvInvoke.PutText(image, "Hello, world", new System.Drawing.Point(10, 50), Emgu.CV.CvEnum.FontFace.HersheyPlain, 3.0, new Bgr(255.0, 0.0, 0.0).MCvScalar);

            image1.Source = image.ToBitmapSource();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var v = new CameraCaptureForm();
            System.Windows.Forms.Application.EnableVisualStyles();
            System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
            System.Windows.Forms.Application.Run(v);
        }
        #region Video
        private VideoCapture _capture = null;
        private bool _captureInProgress;

        public bool IsInitFirst { get; private set; } = false;

        Mat _frame;
        private Mat _grayFrame;
        private Mat _smallGrayFrame;
        private Mat _smoothedGrayFrame;
        private Mat _cannyFrame;
        private void ReleaseData()
        {
            if (_capture != null)
                _capture.Dispose();
        }
        #endregion
        private async void Button2_Click(object sender, RoutedEventArgs e)
        {
            var captureButton = sender as System.Windows.Controls.Button;

            if (captureButton == null)
            {
                return;
            }
            captureButton.IsEnabled = false;
            this.Cursor = System.Windows.Input.Cursors.Wait;

            var currTask = Task.Run(() =>
            {
                if (!IsInitFirst)
                {
                    IsInitFirst = true;
                    CvInvoke.UseOpenCL = false;
                    try
                    {
                        _frame = new Mat();
                        _grayFrame = new Mat();
                        _smallGrayFrame = new Mat();
                        _smoothedGrayFrame = new Mat();
                        _cannyFrame = new Mat();

                        _capture = new VideoCapture();
                        App.Invoke((Action)(() =>
                        {
                            lblMsg.Content = "FrameRate:" + _capture.Get(Emgu.CV.CvEnum.CapProp.Fps);

                        }));
                        _capture.ImageGrabbed += ProcessFrame;
                    }
                    catch (NullReferenceException excpt)
                    {
                        System.Windows.MessageBox.Show(excpt.Message);
                    }
                }

                if (_capture != null && captureButton != null)
                {
                    if (_captureInProgress)
                    {  //stop the capture
                        App.Invoke(() =>
                        {
                            captureButton.Content = "Start Capture";
                        });
                        _capture.Pause();
                    }
                    else
                    {
                        //start the capture
                        App.Invoke(() =>
                        {
                            captureButton.Content = "Stop";
                        });
                        _capture.Start();
                    }

                    _captureInProgress = !_captureInProgress;
                }
            });
            await currTask.ContinueWith(t =>
            {
                App.Invoke(() =>
                {
                    captureButton.IsEnabled = true;
                    this.Cursor = System.Windows.Input.Cursors.Arrow;
                });
            });
        }
        private void ProcessFrame(object? sender, EventArgs e)
        {
            try
            {
                if (App.Current == null)
                {
                    return;
                }

                if (_capture != null && _capture.Ptr != IntPtr.Zero)
                {
                    _capture.Retrieve(_frame, 0);

                    CvInvoke.CvtColor(_frame, _grayFrame, ColorConversion.Bgr2Gray);

                    CvInvoke.PyrDown(_grayFrame, _smallGrayFrame);

                    CvInvoke.PyrUp(_smallGrayFrame, _smoothedGrayFrame);

                    CvInvoke.Canny(_smoothedGrayFrame, _cannyFrame, 100, 60);

                    App.Current.Dispatcher.Invoke(() =>
                    {
                        image1.Source = _frame.ToBitmapSource();
                        image2.Source = _grayFrame.ToBitmapSource();
                        image3.Source = _smoothedGrayFrame.ToBitmapSource();
                        image4.Source = _cannyFrame.ToBitmapSource();
                    });


                    //使用高斯滤波去除噪声
                    CvInvoke.GaussianBlur(_grayFrame, _grayFrame, new System.Drawing.Size(5, 5), 3);

                    HoughCircles(_frame, _grayFrame);

                    HoughLinesP(_frame, _grayFrame);

                    RectangleImage(_frame, _grayFrame);


                    HOGDescriptorToDo(_frame, _grayFrame);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        /// <summary>
        /// 霍夫圆检测
        /// </summary>
        void HoughCircles(Mat src, Mat grayFrame)
        {
            //霍夫圆检测
            CircleF[] circles = CvInvoke.HoughCircles(grayFrame, HoughModes.Gradient, 2.0, 20.0, 100.0, 180.0, 5);

            // draw circles
            Image<Bgr, Byte> circleImage = src.ToImage<Bgr, byte>();
            foreach (CircleF circle in circles)
                circleImage.Draw(circle, new Bgr(System.Drawing.Color.Blue), 4);
            //CvInvoke.Imshow("HoughCircles", circleImage);
            App.Invoke(() =>
            {
                image5.Source = circleImage.ToBitmapSource();
            });

        }
        /// <summary>
        /// 霍夫线检测
        /// </summary>
        /// <param name="src"></param>
        /// <param name="grayFrame"></param>
        void HoughLinesP(Mat src, Mat grayFrame)
        {
            #region Lines detection
            UMat cannyEdges = new UMat();
            CvInvoke.Canny(grayFrame, cannyEdges, 100, 120);
            //CvInvoke.Imshow("Canny Image", cannyEdges);
            LineSegment2D[] lines = CvInvoke.HoughLinesP(cannyEdges, 1, Math.PI / 20.0, 30, 80, 30);
            #endregion

            Image<Bgr, Byte> lineImage = src.ToImage<Bgr, byte>();
            foreach (LineSegment2D line in lines)
                lineImage.Draw(line, new Bgr(System.Drawing.Color.HotPink), 2);

            //CvInvoke.Imshow("HoughLinesP", lineImage);
            App.Invoke(() =>
            {
                image6.Source = lineImage.ToBitmapSource();
            });
        }
        /// <summary>
        /// 矩形三角形检测
        /// </summary>
        void RectangleImage(Mat src, Mat grayFrame)
        {
            #region Canny and edge detection
            UMat cannyEdges = new UMat();
            CvInvoke.Canny(grayFrame, cannyEdges, 60, 180);
            //CvInvoke.Imshow("Canny Image", cannyEdges);
            #endregion

            #region Find triangles and rectangles
            List<Triangle2DF> triangleList = new List<Triangle2DF>();
            List<RotatedRect> boxList = new List<RotatedRect>(); //旋转的矩形框

            using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
            {
                CvInvoke.FindContours(cannyEdges, contours, null, RetrType.List, ChainApproxMethod.ChainApproxSimple);
                int count = contours.Size;
                for (int i = 0; i < count; i++)
                {
                    using (VectorOfPoint contour = contours[i])
                    using (VectorOfPoint approxContour = new VectorOfPoint())
                    {
                        CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.08, true);
                        //仅考虑面积大于50的轮廓
                        if (CvInvoke.ContourArea(approxContour, false) > 50)
                        {
                            if (approxContour.Size == 3) //轮廓有3个顶点：三角形
                            {
                                System.Drawing.Point[] pts = approxContour.ToArray();
                                triangleList.Add(new Triangle2DF(pts[0], pts[1], pts[2]));
                            }
                            else if (approxContour.Size == 4) //轮廓有4个顶点
                            {
                                #region determine if all the angles in the contour are within [80, 100] degree
                                bool isRectangle = true;
                                System.Drawing.Point[] pts = approxContour.ToArray();
                                LineSegment2D[] edges = Emgu.CV.PointCollection.PolyLine(pts, true);

                                for (int j = 0; j < edges.Length; j++)
                                {
                                    double angle = Math.Abs(edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j]));
                                    if (angle < 80 || angle > 100)
                                    {
                                        isRectangle = false;
                                        break;
                                    }
                                }
                                #endregion
                                if (isRectangle) boxList.Add(CvInvoke.MinAreaRect(approxContour));
                            }
                        }
                    }
                }
            }
            #endregion

            //显示结果
            Image<Bgr, Byte> triangleRectangleImage = src.ToImage<Bgr, byte>().CopyBlank();
            foreach (Triangle2DF triangle in triangleList)
                triangleRectangleImage.Draw(triangle, new Bgr(System.Drawing.Color.DarkBlue), 2);

            //CvInvoke.Imshow("triangleRectangleImage", triangleRectangleImage);
            App.Invoke(() =>
            {
                image7.Source = triangleRectangleImage.ToBitmapSource();
            });

            Image<Bgr, Byte> RectangleImage = src.ToImage<Bgr, byte>().CopyBlank();
            foreach (RotatedRect box in boxList)
                RectangleImage.Draw(box, new Bgr(System.Drawing.Color.DarkOrange), 2);

            //CvInvoke.Imshow("RectangleImage", RectangleImage);
            App.Invoke(() =>
            {
                image8.Source = RectangleImage.ToBitmapSource();
            });
        }
        /// <summary>
        /// 行人检测
        /// </summary>
        public void HOGDescriptorToDo(Mat src, Mat grayFrame)
        {
            var image = src.ToImage<Bgr, byte>();

            MCvObjectDetection[] regions;
            using (HOGDescriptor des = new HOGDescriptor())
            {
                des.SetSVMDetector(HOGDescriptor.GetDefaultPeopleDetector());
                regions = des.DetectMultiScale(image);
            }
            foreach (MCvObjectDetection pedestrain in regions)
                image.Draw(pedestrain.Rect, new Bgr(System.Drawing.Color.Blue), 2);
            //CvInvoke.Imshow("image", image);

            App.Invoke(() =>
            {
                image9.Source = image.ToBitmapSource();
            });
        }
        public void Dispose()
        {
            ReleaseData();
        }
    }
}